use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use chrono::{DateTime, Local};
use linked_hash_map::LinkedHashMap;
use crate::codec::RpcCodecGather;
use crate::connection_manager::event::IdleListener;
use crate::{LocalSeqCreator, RpcFilter};
use crate::codec::binary_codec::RpcBinCodeCodec;
use crate::codec::json_codec::RpcJsonCodec;
use crate::router::{RouterFactory, RpcRouter};

pub struct Bootstrap {
    pub router: RpcRouter,
    pub buff_size: usize,
    pub rpc_codec_map: RpcCodecGather,
    pub local_seq_creator: Option<Arc<Box<dyn LocalSeqCreator>>>,
    pub idle_listeners: Arc<Vec<Box<dyn IdleListener>>>,
}

impl Bootstrap {
    pub fn get_router(&self, path: &'static str) ->  Option<&Box<dyn RouterFactory + 'static>> {
        self.router.get_router(path)
    }

    pub fn get_filters(&self)-> Arc<LinkedHashMap<&'static str, Box<dyn RpcFilter + 'static>>> {
        self.router.get_filters()
    }

    pub fn date_time_format(&self)-> String {
        let now = SystemTime::now();
        if let Ok(duration) = now.duration_since(UNIX_EPOCH) {
            let milliseconds = duration.as_secs() * 1000 + u64::from(duration.subsec_millis());
            format!("{}", milliseconds)
        } else {
            let local: DateTime<Local> = DateTime::from(now);
            let time_format = local.format("%Y-%m-%d %H:%M:%S");
            time_format.to_string()
        }
    }
}

pub struct BootstrapBuilder {
    routers: HashMap<&'static str, Box<dyn RouterFactory + 'static>>,
    filters: LinkedHashMap<&'static str, Box<dyn RpcFilter + 'static>>,
    buff_size: usize,
    codec_type_map: HashMap<String, Box<dyn Any>>,
    local_seq_creator: Option<Box<dyn LocalSeqCreator>>,
    idle_listeners: Option<Vec<Box<dyn IdleListener>>>,
}

impl BootstrapBuilder {

    pub fn new() -> Self {
        let builder = BootstrapBuilder {
            routers: HashMap::new(),
            filters: LinkedHashMap::new(),
            buff_size: 4096,
            codec_type_map: HashMap::new(),
            local_seq_creator: None,
            idle_listeners: None,
        }
            .add_rpc_codec_factory("json", Box::new(RpcJsonCodec::default()))
            .add_rpc_codec_factory("bin-code", Box::new(RpcBinCodeCodec::default()));
        builder
    }

    pub fn router(self, path: &'static str, router_factory: Box<dyn RouterFactory + 'static>) -> Self {
        let mut routers = self.routers;
        routers.insert(path, router_factory);
        Self {
            routers,
            ..self
        }
    }

    pub fn filter(self, expression: &'static str, filter: Box<dyn RpcFilter + 'static>) -> Self {
        let mut filters = self.filters;
        filters.insert(expression, filter);
        Self {
            filters,
            ..self
        }
    }

    pub fn add_rpc_codec_factory(self, codec_type: &'static str, object_code: Box<dyn Any>) -> Self {
        let mut codec_type_map = self.codec_type_map;
        codec_type_map.insert(codec_type.to_string(), object_code);
        Self {
            codec_type_map,
            ..self
        }
    }

    pub fn add_idle_listener(self, idle_listener: Box<dyn IdleListener>) -> Self {
        let mut idle_listeners = self.idle_listeners.unwrap_or_default();
        idle_listeners.push(idle_listener);
        Self {
            idle_listeners: Some(idle_listeners),
            ..self
        }
    }

    pub fn set_local_seq_creator(self, local_seq_creator: Box<dyn LocalSeqCreator>) -> Self {
        Self {
            local_seq_creator: Some(local_seq_creator),
            ..self
        }
    }

    pub fn buff_size(self, size: usize) -> Self {
        Self {
            buff_size: size,
            ..self
        }
    }

    pub fn build(self) -> Bootstrap {
        let local_seq_creator = match self.local_seq_creator {
            None => { None }
            Some(local_seq_creator) => { Some(Arc::new(local_seq_creator))}
        };
        
        let idle_listeners = self.idle_listeners.unwrap_or_else(|| { Vec::new() });
        Bootstrap {
            router: RpcRouter::new(self.routers, self.filters,),
            buff_size: self.buff_size,
            rpc_codec_map: RpcCodecGather::new(self.codec_type_map),
            local_seq_creator,
            idle_listeners: Arc::new(idle_listeners),
        }
    }
}

